import os
import sys
import time
import warnings
from datetime import datetime
from datetime import date
from urllib.parse import urljoin
import pandas as pd

from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

from .captcha import *
from .config import *
from .logger import *
from .network import *
from .screenshot import *
from .file_manager import *
from .decorator import *
import random

warnings.filterwarnings("ignore")


class BaseDownloader:

    @log_function_call
    def __init__(self):
        self.verify_required_files()
        self.config = load_configuration()
        self.downloads_dir = self.initialize_tmp_downloads_directory()
        self.current_date = datetime.now().strftime("%Y-%m-%d")
        self.headless_mode = get_headless_mode()
        self.browser_name = "UnknownBrowser"
        self.twocaptcha_api_key = get_twocaptcha_api_key()
        self.site_name = self.get_site_name()
        self.login_success = False
        self.whole_process_success = False
        
        # Add these lines to initialize dates
        dates = get_start_and_end_date()
        self.start_date = dates['start_date']
        self.end_date = dates['end_date']

    def verify_required_files(self):
        """Verify the existence of required configuration files."""
        required_files = [
            "config.yml",
            ".env",
        ]

        for file in required_files:
            if not os.path.isfile(file):
                logger.error(f"Required file not found: {file}")
                sys.exit(1)
            else:
                logger.debug(f"Required file found: {file}")

    @log_function_call
    def prepare_environment(self):
        self.task_download_dir = self.initialize_task_download_directory()
        self.setup_logging()
        self.load_secret_credentials()

        if not validate_site_name_conflicts():
            logger.error(
                "SiteName conflict detected in config.yml. Please resolve the conflicts."
            )
            sys.exit(1)

        self.copy_config_yml_file()
        self.initialize_webdriver()
        self.setup_screenshot_service()
        self.remove_existing_log_file() 

    def copy_config_yml_file(self):
        source_path = "config.yml"
        destination_path = os.path.join(self.task_download_dir, "config.yml")

        try:
            shutil.copy(source_path, destination_path)
            logger.info(f"Copied config.yml to {destination_path}")
        except Exception as e:
            logger.error(f"An error occurred while copying config.yml: {e}")

    @log_function_call
    def initialize_tmp_downloads_directory(self) -> str:
        tmp_downloads_dir = (
            load_configuration()
            .get("directories", {})
            .get("browser_downloads_directory", "./Downloads-tmp")
        )
        if "~" in tmp_downloads_dir:
            return os.path.expanduser(tmp_downloads_dir)
        else:
            full_path = os.path.join(os.getcwd(), tmp_downloads_dir)
            os.makedirs(full_path, exist_ok=True)
            return full_path

    @log_function_call
    def initialize_task_download_directory(self) -> str:
        task_dir = os.path.join(os.getcwd(), self.current_date)
        os.makedirs(task_dir, exist_ok=True)
        return task_dir

    @log_function_call
    def setup_logging(self) -> None:
        logger_instance.clear_existing_handlers()
        log_filename = f"{self.site_name}.log"
        logger_instance.add_file_handler(self.current_date, log_filename)
        self.logger_file_path = os.path.join(
            os.getcwd(), self.current_date, log_filename
        )

    @log_function_call
    def remove_existing_log_file(self) -> None:
        try:
            if os.path.isfile(self.logger_file_path):
                # Open the file in write mode to empty it
                with open(self.logger_file_path, 'w') as file:
                    pass  # Just opening and closing the file empties it
                logger.info(f"Emptied existing log file: {self.logger_file_path}")
            else:
                logger.warning(f"Warning: log file does not exist: {self.logger_file_path}")
        except Exception as e:
            logger.error(f"Error emptying log file {self.logger_file_path}: {e}")

    @log_function_call
    def load_secret_credentials(self):
        self.task_id = map_site_to_task_id().get(self.get_site_name())

        secret_credentials = get_secret_data_for_task(self.task_id)
        if not secret_credentials:
            return

        self.username = secret_credentials["username"]
        self.password = secret_credentials["password"]
        self.url = secret_credentials["url"]
        self.requires_captcha = secret_credentials["requires_captcha"]

    @log_function_call
    def get_site_name(self) -> str:
        return self.__class__.__name__

    @log_function_call
    def setup_screenshot_service(self):
        self.screenshot_service = ScreenshotService(self.site_name, self.driver)

    @log_function_call
    def capture_screenshot(self):
        screenshot_dir = self.task_download_dir
        self.screenshot_service.capture_screenshot(screenshot_dir)

    @log_function_call
    def main(self):
        self.prepare_environment()
        self.navigate_to_url()
        self.login()
        self.verify_login_success()
        self.retrieve_data()
        self.download_file()
        self.terminate_webdriver()

    @log_function_call
    def navigate_to_url(self):
        self.driver.get(self.url)

    @log_function_call
    def initialize_webdriver_options(self):
        pass

    @log_function_call
    def initialize_webdriver(self):
        pass

    @log_function_call
    def login(self):
        pass

    @log_function_call
    def verify_login_success(self) -> bool:
        # TODO
        pass

    @log_function_call
    def retrieve_data(self):
        pass

    @log_function_call
    def download_file(self):
        pass

    @log_function_call
    def terminate_webdriver(self):
        self.driver.quit()

    @log_function_call
    def wait_for_download_completion(self):
        """Wait until there are no ongoing downloads."""
        while True:
            ongoing_downloads = [
                f
                for f in os.listdir(self.downloads_dir)
                if f.endswith(".part") or f.endswith(".crdownload")
            ]
            if not ongoing_downloads:
                break
            else:
                logger.info("Ongoing downloads detected:")
                logger.info(
                    f"{len(ongoing_downloads)} file(s) in progress: {', '.join(ongoing_downloads)}"
                )
                logger.info(
                    "Sleeping for 1 second. Ensure no unrelated downloads to avoid conflicts."
                )
                time.sleep(1)

    @log_function_call
    def get_latest_downloaded_files(self, num_files: int) -> list[str]:
        self.wait_for_download_completion()
        return get_latest_downloaded_files(self.downloads_dir, num_files)

    @log_function_call
    def get_latest_downloaded_file(self) -> str:
        return get_most_recent_downloaded_file(self.downloads_dir)

    @log_function_call
    def navigate_to_relative_url(self, relative_href: str):
        current_url = self.driver.current_url
        logger.info(f"Current URL: {current_url}")

        new_url = urljoin(current_url, relative_href)
        logger.info(f"Navigating to: {new_url}")
        self.driver.get(new_url)

    @log_function_call
    def accept_alert_dialog(self):
        try:
            WebDriverWait(self.driver, 10).until(EC.alert_is_present())
            alert = self.driver.switch_to.alert
            alert.accept()
            logger.info("Accepted alert dialog.")
        except TimeoutException:
            logger.info("No alert dialog appeared within the timeout period.")
        except Exception as e:
            logger.exception(f"Error handling alert dialog: {e}")

    @log_function_call
    def random_sleep(self,base):
        extra_sleep_time = random.uniform(0,1)
        total_sleep_time = base + extra_sleep_time
        time.sleep(total_sleep_time)
